﻿// -------------------------------------------------------------------------
//    @FileName         :    NFCacheDataMgr.cpp.cpp
//    @Author           :    Gao.Yi
//    @Date             :    2025-08-11
//    @Email			:    445267987@qq.com
//    @Module           :    NFCacheDataMgr.cpp
//    @Desc             :    缓存数据管理器实现
//                           提供缓存对象的获取、创建、续期、淘汰、批量加载等统一管理能力
//                           支持基于访问续期和创建兜底超时的双重过期机制
//
// -------------------------------------------------------------------------

#include "NFCacheDataMgr.h"

#include "NFCacheData.h"
#include "NFCacheDataGetTrans.h"
#include "NFComm/NFCore/NFServerTime.h"

/**
 * @brief 构造并根据共享内存模式初始化
 * 
 * 根据共享内存管理器的创建模式选择初始化方式：
 * - EN_OBJ_MODE_INIT：冷启动模式，调用 CreateInit() 进行完整初始化
 * - 其他模式：热启动模式，调用 ResumeInit() 进行恢复初始化
 */
NFCacheDataMgr::NFCacheDataMgr()
{
    if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
    {
        CreateInit();
    }
    else
    {
        ResumeInit();
    }
}

/** 
 * @brief 析构函数
 * 
 * 清理资源，由于使用共享内存管理，通常无需特殊处理
 */
NFCacheDataMgr::~NFCacheDataMgr()
{
}

/** 
 * @brief 冷启动初始化
 * 
 * 在共享内存冷启动场景下，对新创建的管理器对象进行初始化
 * 当前实现为空，如有需要可在此添加初始化逻辑
 * 
 * @return 0 表示成功
 */
int NFCacheDataMgr::CreateInit()
{
    return 0;
}

/** 
 * @brief 热启动恢复初始化
 * 
 * 在共享内存热启动场景下，从持久化数据恢复管理器状态时调用
 * 当前实现为空，如有需要可在此添加恢复逻辑
 * 
 * @return 0 表示成功
 */
int NFCacheDataMgr::ResumeInit()
{
    return 0;
}

/**
 * @brief 获取缓存对象（不续期）
 * 
 * 根据对象类型和ID获取缓存对象，不会改变对象的过期时间。
 * 适用于只读访问场景，避免不必要的续期操作。
 * 
 * @param objType 缓存对象的类型
 * @param ullId   缓存对象的唯一标识ID
 * @return 指向缓存对象的指针，若未找到则返回 nullptr
 */
NFCacheData* NFCacheDataMgr::GetCacheData(int objType, uint64_t ullId)
{
    return dynamic_cast<NFCacheData*>(FindModule<NFIMemMngModule>()->GetObjByHashKey(objType, ullId));
}

/**
 * @brief 新增或覆盖缓存对象
 * 
 * 完整的缓存对象管理逻辑：
 * 1. 验证输入参数并提取主键ID
 * 2. 尝试获取现有对象，如果存在则续期并更新数据
 * 3. 如果高负载则触发扫描淘汰一批过期对象
 * 4. 创建新对象并初始化数据
 * 5. 设置访问续期过期时间
 * 
 * @param objType   缓存对象的类型
 * @param pMessage  指向 Protobuf 消息对象的指针，用于初始化缓存对象
 * @return 指向缓存对象的指针，若失败则返回 nullptr
 */
NFCacheData* NFCacheDataMgr::AddCacheData(int objType, const google::protobuf::Message* pMessage)
{
    CHECK_NULL_RET_VAL(0, pMessage, nullptr);
    std::string result;
    int iRetCode = NFProtobufCommon::GetPrimarykeyFromMessage(pMessage, result);
    CHECK_ERR_RE_NULL(0, iRetCode, "GetPrimarykeyFromMessage:{} failed. id:{}", pMessage->GetTypeName(), result);
    uint64_t ullId = NFCommon::strto<uint64_t>(result);

    LOG_INFO(ullId, "AddCacheData objType:{} ullId:{}", objType, ullId);

    // 尝试获取现有对象，如果存在则续期并更新数据
    auto pCacheData = GetCacheDataAndSetExpire(objType, ullId);
    if (pCacheData)
    {
        pCacheData->InitDataFromMessage(pMessage);
        return pCacheData;
    }

    // 高负载时触发扫描淘汰过期对象
    if (FindModule<NFIMemMngModule>()->GetUsedCount(objType) > (FindModule<NFIMemMngModule>()->GetItemCount(objType) * CACHE_DATA_LOAD_FACTOR))
    {
        int iMiniScanNum = FindModule<NFIMemMngModule>()->GetItemCount(objType) * CACHE_DATA_SCAN_FACTOR;
        if (iMiniScanNum < CACHE_DATA_MIN_SCAN_NUM)
        {
            iMiniScanNum = CACHE_DATA_MIN_SCAN_NUM;
        }

        RemoveExpiredCacheData(objType, iMiniScanNum);
    }

    // 创建新对象并初始化
    pCacheData = dynamic_cast<NFCacheData*>(FindModule<NFIMemMngModule>()->CreateObjByHashKey(objType, ullId));
    CHECK_NULL_RE_NULL(0, pCacheData, "CreateObjByHashKey failed. objType:{} Id:{}", objType, ullId);

    pCacheData->InitDataFromMessage(pMessage);
    pCacheData->SetCacheExpireTime();

    return pCacheData;
}

/**
 * @brief 获取并续期对象
 * 
 * 根据对象类型和ID获取缓存对象，并更新其过期时间（滑动过期）。
 * 适用于读写访问场景，确保频繁访问的对象不会被过早淘汰。
 * 
 * @param objType 缓存对象的类型
 * @param ullId   缓存对象的唯一标识ID
 * @return 指向缓存对象的指针，若未找到则返回 nullptr
 */
NFCacheData* NFCacheDataMgr::GetCacheDataAndSetExpire(int objType, uint64_t ullId)
{
    auto pCacheData = GetCacheData(objType, ullId);
    if (pCacheData != nullptr)
    {
        pCacheData->SetCacheExpireTime();
    }
    return pCacheData;
}

/**
 * @brief 获取最近有效对象（若创建兜底已超时则返回空）
 * 
 * 根据对象类型和ID获取最近有效的缓存对象。
 * 如果对象的创建兜底时间已超时，则返回空指针，即使访问续期时间未到。
 * 
 * @param objType 缓存对象的类型
 * @param ullId   缓存对象的唯一标识ID
 * @return 指向缓存对象的指针，若未找到或超时则返回 nullptr
 */
NFCacheData* NFCacheDataMgr::GetRecentCacheData(int objType, uint64_t ullId)
{
    auto pCacheData = GetCacheData(objType, ullId);
    if (pCacheData != nullptr)
    {
        if (pCacheData->IsCacheTimeOut())
        {
            return nullptr;
        }
    }
    return pCacheData;
}

/**
 * @brief 批量加载缓存数据（过滤掉已有效的ID）
 * 
 * 批量加载缓存数据的完整流程：
 * 1. 遍历ID列表，过滤掉已经有效的缓存对象
 * 2. 如果所有ID都已有效，直接返回成功
 * 3. 创建批量获取事务，发起异步加载请求
 * 4. 事务完成后会自动将数据添加到缓存中
 * 
 * @param serverType        目标存储服务器类型
 * @param objType           缓存对象类型
 * @param getCacheTransType 获取事务类型（用于创建具体拉取事务）
 * @param loadCacheTransType 加载事务类型
 * @param iTableId          数据表ID
 * @param roleId            角色ID
 * @param vecId             需要拉取的ID列表
 * @param iReason           调用原因
 * @return 0 表示成功，其他值为错误码
 */
int NFCacheDataMgr::BatchLoadCacheData(int serverType, int objType, int getCacheTransType, int loadCacheTransType, int iTableId, uint64_t roleId, const std::vector<uint64_t>& vecId, int iReason)
{
    // 过滤掉已经有效的ID
    std::vector<uint64_t> leftVec;
    for (size_t i = 0; i < vecId.size(); i++)
    {
        uint64_t ullId = vecId[i];
        auto pCacheData = GetRecentCacheData(objType, ullId);
        if (!pCacheData)
        {
            leftVec.push_back(ullId);
        }
    }

    // 如果所有ID都已有效，直接返回成功
    if (leftVec.empty())
    {
        return 0;
    }

    // 创建批量获取事务并发起异步加载
    NFCacheDataGetTrans* pTrans = dynamic_cast<NFCacheDataGetTrans*>(FindModule<NFIMemMngModule>()->CreateTrans(getCacheTransType));
    CHECK_NULL_WF(0, pTrans, "CreateTrans failed, used num:{} all num:{}", FindModule<NFIMemMngModule>()->GetUsedCount(getCacheTransType), FindModule<NFIMemMngModule>()->GetItemCount(getCacheTransType));
    pTrans->Init(serverType, roleId, iTableId);
    int iRetCode = pTrans->GetCacheDataBatchReq(loadCacheTransType, leftVec, iReason);
    CHECK_ERR_AND_FIN_TRANS(iRetCode, pTrans, "GetRoleSimpleInfoBatchReq failed, roleId:{}", NFCommon::tostr(vecId));
    return 0;
}

/**
 * @brief 移除指定的缓存对象
 * 
 * 从缓存中移除给定的缓存对象，释放其占用的内存空间。
 * 
 * @param pCacheData 指向需要移除的缓存对象的指针
 * @return 0 表示成功，其他值为错误码
 */
int NFCacheDataMgr::RemoveCacheData(NFCacheData* pCacheData)
{
    if (pCacheData)
    {
        LOG_DEBUG(pCacheData->GetCacheId(), "RemoveCacheData className:{} id:{}", pCacheData->GetClassName(), pCacheData->GetCacheId());
        FindModule<NFIMemMngModule>()->DestroyObj(pCacheData);
    }
    return 0;
}

/**
 * @brief 扫描并移除过期对象
 * 
 * 使用轮询扫描的方式移除过期对象：
 * 1. 使用静态迭代器记录上次扫描位置，实现轮询扫描
 * 2. 检查每个对象的访问续期时间是否已过期
 * 3. 移除过期的对象，释放内存空间
 * 4. 返回实际移除的对象数量
 * 
 * @param objType   对象类型
 * @param iScanNum  本次扫描数量上限
 * @return 实际移除数量
 */
int NFCacheDataMgr::RemoveExpiredCacheData(int objType, int iScanNum)
{
    int iNum = 0;
    uint32_t dwTimeNow = NF_ADJUST_TIMENOW();
    static auto staticLastIter = FindModule<NFIMemMngModule>()->IterBegin(objType);
    for (int i = 0; i < iScanNum; i++)
    {
        if (!FindModule<NFIMemMngModule>()->IsValid(staticLastIter) || staticLastIter == FindModule<NFIMemMngModule>()->IterEnd(objType))
        {
            staticLastIter =  FindModule<NFIMemMngModule>()->IterBegin(objType);
            if (staticLastIter == FindModule<NFIMemMngModule>()->IterEnd(objType))
            {
                break;
            }
            if (i > FindModule<NFIMemMngModule>()->GetUsedCount(objType))
            {
                break;
            }
        }

        auto pCacheData= dynamic_cast<NFCacheData*>(&*staticLastIter);
        CHECK_NULL(0, pCacheData);

        // 检查访问续期时间是否已过期
        if (dwTimeNow > pCacheData->GetCacheExpireTime())
        {
            RemoveCacheData(pCacheData);
            ++iNum;
        }
    }
    return iNum;
}

/**
 * @brief 判断对象是否存在
 * 
 * 检查指定类型和ID的缓存对象是否存在。
 * 
 * @param objType 缓存对象的类型
 * @param ullId   缓存对象的唯一标识ID
 * @return true 表示存在，false 表示不存在
 */
bool NFCacheDataMgr::IsExistCacheData(int objType, uint64_t ullId)
{
    if (GetCacheData(objType, ullId))
    {
        return true;
    }
    return false;
}
